#include "list.h"
using namespace std;
//TIP:http://www.scs.stanford.edu/~dm/home/papers/c++-new.html

//constructor
list::list(){
    //assigning init alues
    len = 0;

    //declare pointers
    wRoot = allocate();

    wCurrent = wRoot;

    curpos = 0;
}

//destructor
list::~list(){
    wCurrent = findLast();

    //deal with NULL pointer
    if(wCurrent == (item*) NULL){throw "destruction of class failed";}

    //delete all children
    while(wCurrent!=wRoot){
        if(DEBUG>1){cout << "killing: " << wCurrent << endl;}
        delete wCurrent->content;
        wCurrent = wCurrent->prev;
        delete wCurrent->next;
    }

    //delete root
    if(DEBUG>1){cout << "killing root: " << wCurrent << endl;}
    delete wRoot->content;
    delete wRoot;
}

/** @fn     list::allocate()
    @brief  allocates memory space and sets default values for a new list item
    @param  none
    @return pointer to the new item
*/
item* list::allocate(){
    //allocate space
    item* wItem = new item;

    //deal with NULL pointer
    if(wItem == (item*) NULL){throw "allocation error"; return 0;}

    //assign default values to root
    wItem->child = (item*) NULL;
    wItem->content = (char*) NULL;
    wItem->count = 0;
    wItem->len = 0;
    wItem->next = (item*) NULL;
    wItem->parent = (item*) NULL;
    wItem->prev = (item*) NULL;
    len++;

    return(wItem);
}
/** @fn     list::print()
    @brief  prints out the list
    @param  void
    @return void
*/
void list::print(void){
    int items = 0;
    //walkthrough list
    wCurrent = wRoot;
    //if current list item contains any data
    while(wCurrent != (item*) NULL){
        //print data
        printItem(wCurrent);
        items++;
        //jump to next item in list
        wCurrent = wCurrent->next;
    }
    if(DEBUG>1){cout << "items count: " << items << endl;}
}

/** @fn     list::printItem()
    @brief  prints out a single item in the list
    @param  wPrint      the pointer to the list item to be printed
    @return void
*/
void list::printItem(item * wPrint){
    //if wPrint is not set use the class var wCurrent
    if(wPrint == (item*) NULL){wPrint = wCurrent;}
    //if the item contains any data, print the item
    if(wPrint->content != (char*) NULL){
        cout << endl;
        cout << "addr: " << hex<<wPrint << endl;
        //cout << "prnt: " << hex<<wPrint->parent << endl;
        //cout << "chld: " << hex<<wPrint->child << endl;
        cout << "next: " << hex<<wPrint->next << endl;
        cout << "prev: " << hex<<wPrint->prev << endl;
        cout << "cont: " << hex<<&wPrint->content << ":" << wPrint->content << endl;
        cout << "len:  " << dec<<wPrint->len << endl;
        cout << "cnt:  " << dec<<wPrint->count << endl;
    }
}

/** @fn     list::add(string* str)
    @brief  adds the string str to the list
    @param  str     the string to be added to the list
    @return pointer to the recently added item
*/
item* list::add(char* str){
    if(DEBUG>1){cout << endl << "adding:" << str << endl;}

    //find the first free item in the list
    wCurrent = findFree();
    //if free item found, populate
    if(wCurrent != (item*) NULL){
        itemPopulate(str);
        return(wCurrent);
    }
    //if no free item found in existing list
    //find the last item in the list
    item* wTemp = findLast();
    //throw exception if a last item doesn't exist
    if(wTemp == (item*) NULL){throw "empty item pointer for last item";}
    //init new item (which will be appended to list after the last item)
    wCurrent = allocate();
    //if init succeeded
    if(wCurrent != (item*) NULL){
        //set references
        wTemp->next = wCurrent;
        wCurrent->prev = wTemp;

        if(DEBUG>1){cout << "new item @ " << wCurrent << endl;}
        //populate
        itemPopulate(str);
        return(wCurrent);
    }else{
        throw "adding value to list failed";
    }
}

/** @fn     list::remove(item* wDelete)
    @brief  removes item wDelete from the linked list
    @param  wDelete     the item to be deleted
    @return pointer to the recently added item
*/
int list::remove(item* wDelete){
    if(wDelete == (item*) NULL){throw "error removing item";}

    item* wPrev = wDelete->prev;
    item* wNext = wDelete->next;

    //reassign node pointers
    if(wPrev != (item*) NULL){wPrev->next = wNext;}
    if(wNext != (item*) NULL){wNext->prev = wPrev;}

    if(DEBUG>1){cout << "removing" << hex<<wDelete << endl;}

    //freeing memory
    delete wDelete->content;
    delete wDelete;

    len--;

    return(1);
}

/** @fn     struct item* list::findLast()
    @brief  finds the last item in the linked list
    @param  none
    @return pointer to the last found item or NULL
    */
item* list::findLast(){
    if(wRoot == (item*) NULL){throw "cannot find last item in list without root"; return 0;}

    item *wTemp;
    wTemp = wRoot;

    //run through list until last item is found
    while(wTemp->next != (item*) NULL){
        wTemp = wTemp->next;
    }
    return(wTemp);
}

/** @fn     struct item *findFree()
    @brief  finds the next free item the linked list
    @param  none
    @return pointer of the first vacant item or NULL
    */
item* list::findFree(void){
    //walkthrough list
    wCurrent = wRoot;
    while(wCurrent != (item*) NULL){
        //if empty content space found
        if(wCurrent->content == (char*) NULL){
            if(DEBUG>1){cout << "free item found @ " << hex<<wCurrent << endl;}
            return(wCurrent);
        }
        wCurrent = wCurrent->next;
    }
    return((item*) 0);
}

/** @fn     int list::sort()
    @brief  sorts the items in the list object alphabetically
    @param  void
    @return void
    */
void list::sort(void){
    sortBubble(wRoot, 1);
}

/** @fn     int list::purgeclones()
    @brief  removes double entries from the list object
    @param  void
    @return state (1 for success)
    */
int list::purgeclones(void){
    item* wClone = detectClones(wRoot);
    int i = 0;
    while(wClone != (item*) NULL){
        if(DEBUG>1){cout << "clone @ " << hex<<wClone << " to " << hex<<wClone->prev << endl;}
        list::remove(wClone);
        wClone = detectClones(wRoot);
        i++;
    }

    return(1);
}

/** @fn     int itemPopulate(char *string, item *wRoot)
    @brief  adds a string to the list by either creating a new item or populating a emtpy item
    @param  string  the content to be placed into the item
    @param  wRoot   the pointer of the list
    @return 2 on populating new item, 1 on populating existing item, NULL on no populate
    */
int list::itemPopulate(char* str){
    if(wCurrent == (item*) NULL){cout << "cannot populate empty item" << endl; return 0;}
    if(DEBUG>2){cout << "populate @ " << wCurrent << endl;}
    //set content
    wCurrent->content = str;
    wCurrent->count++;
    wCurrent->len = strlen(str);
    return 1;
}

/** @fn     int list::count()
    @brief  counts the number of items in the list object
    @param  none
    @return the amount of records found
    */
int list::count(void){
    if(!wRoot){throw "empty item pointer";}
    item *wTemp = wRoot;
    int i = 1;
    //count the amount of items in list
    while(wTemp->next){
        i++;
        wTemp = wTemp->next;
    }
    len = i;
    return(i);
}

/** @fn     void list::writedata(fstream& dest)
    @brief  sends a formatted string of data to a filestream
    @param  dest    the filestream for the destination file
    @return void
    */
void list::writedata(fstream& dest){
    int counter = 1;

    //start at the root
    item* wPrint = wRoot;

    //loop through list
    while(wPrint != (item*) NULL){
        dest << counter << DELIMITER << wPrint->content << DELIMITER << wPrint->count << endl;
        wPrint = wPrint->next;
        counter++;
    }
    counter = 0;
}

/** @fn     string list::getPrompt(int direction)
    @brief  steps through the list and returns a formatted string of data for a UI
    @param  direction of the step to be made
    @return the string object pointer to the formatted UI string
    */
std::string list::getPrompt(int direction){
    std::ostringstream strPromptstr;
    //if no direction specified
    if(direction == 0){
        strPromptstr << "-" << curpos << ": " << wCurrent->content << " " << wCurrent->count << "x";
    }
    //if direction is upwards
    if(direction>0){
        if(wCurrent->next != (item*) NULL){
            wCurrent = wCurrent->next;
            curpos++;
            strPromptstr << ">" << curpos << ": " << wCurrent->content << " " << wCurrent->count << "x";
        }
    }
    //if direction is downwards
    if(direction<0){
        if(wCurrent->prev != (item*) NULL){
            wCurrent = wCurrent->prev;
            curpos--;
            strPromptstr << "<" << curpos << ": " << wCurrent->content << " " << wCurrent->count << "x";
        }
    }

    return strPromptstr.str();
}
